const crypto = require('crypto')

class h_user {

	async checkLogin(session) {
		if(/([a-f\d]{8}(-[a-f\d]{4}){3}-[a-f\d]{12}?)/i.test(session)) {
			let user = await this.redis.session.get(session, true)
			if(user && user.type && user.userId && user.token && user.data) {
				let userData
				switch(user.type) {
					case 'inside':

					break
					case 'agent':

					break
					case 'wxapp':
						userData = await this._getWxUserDataByUserId(user.userId)
					break
					case 'client':

					break
					case 'store':

					break
					default:
						return this.reject('-2002', 'user login type invalid')
				}
				if(!userData) {
					await this.redis.session.delete(session)
					return this.reject('-2012', 'user not found')
				}
				let {userHead, userName, userSex} = userData
				return this.resolve({
					userHead,
					userName,
					userSex
				})
			}
			else {
				return this.reject('-2000', 'user not logined in')
			}
		}
		else {
			return this.reject('-1011', 'user session invalid')
		}
	}

	async login(loginType, loginData) {
		let result
		switch(loginType) {
			case 'inside':

			break
			case 'agent':

			break
			case 'wxapp':
				result = await this._wxAppLogin(loginData)
			break
			case 'client':

			break
			case 'store':

			break
			default:
				return this.reject('-2002', 'user login type invalid')
		}
		return this.resolve(result)
	}

	async getWxUserDataByUserId(userId) {
		const result = await this._getWxUserDataByUserId(userId)
		if(!result)
			return this.reject('-2028', 'wx user not found')
		return this.resolve(result)
	}

	async _wxAppLogin(loginData) {
		let {wxCode, wxEncrypytedData, wxIv} = loginData
		let wxData = await this._wxLogin(wxCode, wxEncrypytedData, wxIv)
		let {wxUserId, userName, userSex, userLang, userCity, userProvince, userCountry, userHead, sessionKey, expires} = wxData
		let userData = await this._getWxUserDataByWxUserId(wxUserId)
		let userId
		let returnData = null
		//判断是否首次登录
		if(!userData)
			userId = await this._addWxAppUserData(wxUserId, wxData)
		else {
			userId = userData.userId
			await this._updateWxAppUserData(wxUserId, wxData, userData)
		}
		let {session, token} = await this._createSession('wxapp', userId, {wxUserId, sessionKey, expires})
		return {
			session,
			token,
			userData: {
				userHead,
				userName,
				userSex
			}
		}
	}

	async _addWxAppUserData(wxUserId, wxData) {
		let {userName, userSex, userHead, userLang, userCountry, userProvince, userCity} = wxData
		const userId = this.common.md5(wxUserId)
		const result =  await this.mysql.user.insert('wxapp_user', 'userId,wxUserId,userName,userSex,userHead,userLang,userCountry,userProvince,userCity,userDate', [userId, wxUserId, userName, userSex, userHead, userLang, userCountry, userProvince, userCity, this.common.timestamp()])
		if(result == 0) 
			return this.reject('-1024', 'add new wxapp user data failed')
		return userId
	}

	async _updateWxAppUserData(wxUserId, wxData, userData) {
		let updateStr = ''
		let updateData = []
		for(let key in userData) {
			if(wxData[key] && wxData[key] != userData[key]) {
				updateStr += `,${key}=?`
				updateData.push(wxData[key])
			}
		}
		if(updateData.length > 0) {
			console.warn(`user ${userData.userName} data is change`)
			updateData.push(wxUserId)
			await this.mysql.user.update('wxapp_user', this.common.ltrim(updateStr, ','), 'wxUserId=?', updateData)
		}
		else {
			console.log(`user ${userData.userName} data is up-to-date`)
		}
	}

	async _getWxUserDataByWxUserId(wxUserId) {
		if(!wxUserId)
			return this.reject('-2008', 'wx userId invalid')
		let result = await this.mysql.user.select('wxapp_user', 'userId, userName,userRealName,userSex,userHead, userPhone, userFreeze, userDate', 'wxUserId=?', [wxUserId])
		if(result[0]) {
			if(result[0].userFreeze) {
				return this.reject('-2027', 'user is freeze')
			}
			return result[0]
		}
		else {
			return null
		}
	}

	async _getWxUserDataByUserId(userId) {
		let result = await this.mysql.user.select('wxapp_user', 'userName,userRealName,userSex,userHead, userPhone, userFreeze, userDate', 'userId=?', [userId])
		if(result[0]) {
			if(result[0].userFreeze) {
				return this.reject('-2027', 'user is freeze')
			}
			return result[0]
		}
		else {
			return null
		}
	}

	async _wxLogin(wxCode, wxEncrypytedData, wxIv) {
		let {appId, appSecret, grantType} = this.config.miniApp.gtClient
		let {openId, sessionKey, expires} = await this._getSessionKey(appId, appSecret, grantType, wxCode)
		let {nickName, gender, language, city, province, country, avatarUrl} = await this._decryptUserData(appId, sessionKey, openId, wxEncrypytedData, wxIv)
		return {
			wxUserId: openId,
			userName: nickName.length > 60 ? nickName.substring(0, 60) : nickName,
			userSex: gender,
			userLang: language,
			userCity: city,
			userProvince: province,
			userCountry: country,
			userHead: avatarUrl,
			sessionKey,
			expires
		}
	}

	async _getSessionKey(appId, appSecret, grantType, wxCode) {
		try {
			//获取sessionKey
			const result = await this.common.reqGet({
				url: 'https://api.weixin.qq.com/sns/jscode2session',
				data: {
					appid: appId,
					secret: appSecret,
					js_code: wxCode,
					grant_type: grantType
				}
			})
			let {errcode, errmsg, session_key, openid, expires_in} = result
			if(!session_key) {
				console.error(errcode, errmsg)
				return this.reject('-1031', 'get wx session key data failed')
			}
			return {
				sessionKey: session_key,
				openId: openid,
				expires: expires_in
			}
		}
		catch(err) {
			return this.reject('-1021', 'get wx session key data failed')
		}
	}

	async _decryptUserData(appId, sessionKey, openId, wxEncrypytedData, wxIv) {
		sessionKey = Buffer.from(sessionKey, 'base64')
		wxEncrypytedData = Buffer.from(wxEncrypytedData, 'base64')
		wxIv = Buffer.from(wxIv, 'base64')
		let decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, wxIv)
		decipher.setAutoPadding(true)
		let decoded = decipher.update(wxEncrypytedData, 'binary', 'utf8')
		decoded += decipher.final('utf8')
		decoded = JSON.parse(decoded)
		if(!decoded.watermark || decoded.watermark.appid !== appId) {
			return this.reject('-1022', 'appId invalid')
		}
		if(!decoded.openId || decoded.openId !== openId) {
			return this.reject('-1023', 'openId invalid')
		}
		return decoded
	}

	async _createSession(loginType, userId, data) {
		let session = this.common.uuid4()
		let token = await this.common.createRandomStr(10)
		await this.redis.session.set(session, {
			type: loginType,
			userId,
			data,
			token
		}, this.config.frame.sessionExpires[loginType] || this.config.frame.sessionExpires.all || 86400)
		return {
			session,
			token
		}
	}

}

module.exports = h_user